<!DOCTYPE html>
<html>
  <head>
    <!-- 贴图，有几个面，贴几个图 -->
    <!-- <script src="node_modules/three/build/three.js"></script> -->
    <script type="importmap">
      {
        "imports": {
          "three": "./node_modules/three/build/three.module.js",
          "three/addons/": "./node_modules/three/examples/jsm/"
        }
      }
    </script>
    <script type="module">
      import * as THREE from "three";

      // 引入dat.gui.js的一个类GUI
      import { GUI } from "three/addons/libs/lil-gui.module.min.js";
      import { OrbitControls } from "three/addons/controls/OrbitControls.js";
      // 引入gltf模型加载库GLTFLoader.js
      import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";

      // 浏览器控制台测试，是否引入成功
      console.log(THREE.Scene);
      console.log("完成");
      const width = window.innerWidth;
      const height = window.innerHeight;

      // 开始创建
      // 设置场景,坐标原点 0，0，0 中场景中央
      const scene = new THREE.Scene();

      // 点光源
      const pointLight = new THREE.PointLight(0xffffff, 1.0);
      scene.add(pointLight);
      pointLight.position.set(500, 500, 600);

      const geometry = new THREE.BufferGeometry(); //创建一个几何体对象
      const R = 50; //圆弧半径
      const N = 20; //分段数量
      const sp = (2 * Math.PI/2) / N; //两个相邻点间隔弧度
      // 批量生成圆弧上的顶点数据
      const arr = [];
      for (let i = 0; i < N; i++) {
        const angle = sp * i; //当前点弧度
        // 以坐标原点为中心，在XOY平面上生成圆弧上的顶点数据
        const x = R * Math.cos(angle);
        const y = R * Math.sin(angle);
        arr.push(x, y, 0);
      }
      //类型数组创建顶点数据
      const vertices = new Float32Array(arr);
      // 创建属性缓冲区对象
      //3个为一组，表示一个顶点的xyz坐标
      const attribue = new THREE.BufferAttribute(vertices, 3);
      // 设置几何体attributes属性的位置属性
      geometry.attributes.position = attribue;

      // 线材质
      const material = new THREE.LineBasicMaterial({
        color: 0xff0000, //线条颜色
      });
      // 创建线模型对象   构造函数：Line、LineLoop、LineSegments
      // const line = new THREE.Line(geometry, material);
      const line = new THREE.LineLoop(geometry, material); //线条模型对象

      scene.add(line);

      // 添加一个辅助网格地面
      const gridHelper = new THREE.GridHelper(300, 30, 0x004444, 0x004444);
      scene.add(gridHelper);

      // 创建GLTF加载器对象
      const loader = new GLTFLoader();
      loader.load("public/model/cube.glb", function (gltf) {
        console.log("控制台查看加载gltf文件返回的对象结构", gltf);
        console.log("gltf对象场景属性", gltf.scene);
        // 返回的场景对象gltf.scene插入到threejs场景中
        scene.add(gltf.scene);
      });

      const camera = new THREE.PerspectiveCamera(50, width / height, 10, 1000);

      // 设置摄像机的位置
      camera.position.set(0, 60, 200);

      // 设置摄像机的观察点
      camera.lookAt(0, 20, 0);

      // 摄像机从 x -12 向 x 0 方向观察
      console.log(`camera`, camera.position);

      // 设置渲染器
      const renderer = new THREE.WebGLRenderer({
        //想把canvas画布上内容下载到本地，需要设置为true
        preserveDrawingBuffer: true,
      });

      renderer.setSize(width, height);

      // 设置空间背景颜色
      renderer.setClearColor(0xcccccc);
      renderer.render(scene, camera);

      document.body.appendChild(renderer.domElement);

      // 鼠标单击id为download的HTML元素，threejs渲染结果以图片形式下载到本地
      document
        .getElementById("download")
        .addEventListener("click", function () {
          // 创建一个超链接元素，用来下载保存数据的文件
          const link = document.createElement("a");
          // 通过超链接herf属性，设置要保存到文件中的数据
          const canvas = renderer.domElement; //获取canvas对象
          link.href = canvas.toDataURL("image/png");
          link.download = "threejs.png"; //下载文件名
          link.click(); //js代码触发超链接元素a的鼠标点击事件，开始下载文件到本地
        });

      ///*
      const controls = new OrbitControls(camera, renderer.domElement);
      // 相机控件.target属性在OrbitControls.js内部表示相机目标观察点，默认0,0,0
      // console.log('controls.target', controls.target);
      controls.target.set(0, 20, 0);
      controls.update(); //update()函数内会执行camera.lookAt(controls.targe)

      //新版本，加载gltf，不需要执行下面代码解决颜色偏差
      //renderer.outputColorSpace = THREE.SRGBColorSpace;//设置为SRGB颜色空间
      //*/

      // 渲染方法
      function render() {
        // 用渲染器渲染
        renderer.render(scene, camera);

        // 重复渲染，需要查一下
        // 请求动画帧
        // requestAnimationFrame实现周期性循环执行
        // requestAnimationFrame默认每秒钟执行60次，但不一定能做到，要看代码的性能
        requestAnimationFrame(render); // 请求再次执行函数render, 放上面下面都行
        // 浏览器控制台查看相机位置变化
        //console.log('camera.position',camera.position);
        // 浏览器控制台查看controls.target变化，辅助设置lookAt参数
        //console.log('controls.target',controls.target);
      }

      // 执行渲染方法
      render();
    </script>
  </head>
  <body>
    GUI 控件<button type="button" name="button" id="download">下载</button>
    <div id="monitor"></div>
  </body>
</html>
